package gui.view.drawers.editor;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import model.MetroModel;
import model.objects.EdiStation;
import model.objects.EdiTunnel;

import com.nightpiter.controller.log.Logger;
import com.nightpiter.model.objects.Coordinates;
import com.nightpiter.model.objects.Station;
import com.nightpiter.model.objects.Transfer;
import com.nightpiter.model.objects.Transition;
import com.nightpiter.model.objects.Tunnel;


public class StandardDrawer extends AbstractDrawer {
	private static final float SelectionRadiusTransform = 1.3f;
	private int radius;
	private Point drawerCenter = new Point(0,0);		
	private List<PolyStruct> polygons;
	private List<NameStruct> names;
	private float scale=7f;	
	private final float tunnelThikness = 7;
	private final float transferThikness = 7;
	private StandardDrawer drawer = this;
	
	private Font namesFont = new Font("Arial", 0, 13); 
	
	//strokes	
	private Stroke transferStroke1 = new BasicStroke(transferThikness*1.15f);
	private Stroke transferStroke2 = new BasicStroke(transferThikness*1f);
	private Stroke transferStroke3 = new BasicStroke(3.5f);
	private Stroke transferStroke4 = new BasicStroke(transferThikness);
    private Stroke tunnelStroke = new BasicStroke(tunnelThikness);
    private Stroke selectedTunnelStroke = new BasicStroke(tunnelThikness*2f);
   
	
	class PolyStruct {
		
		public EdiTunnel tunnel;
		public Polygon polygon;
		public PolyStruct(EdiTunnel tunnel, Polygon polygon) {			
			this.tunnel = tunnel;
			this.polygon = polygon;
		}
	}
	
	class NameStruct {
		public EdiStation station;
		public Rectangle rect;
		public NameStruct(EdiStation station, Graphics g) {
			this.station = station;			
			String name = station.getName();
			Font font = g.getFont();
			
			Rectangle2D nameRect= font.getStringBounds(name,g.getFontMetrics().getFontRenderContext());
			int width=(int)(nameRect.getWidth());
            int height = (int)(nameRect.getHeight());
			
            int xPosition = drawer.getLocalPoint(station.getPosition()).x + station.getTextRelativePosition().x;
			int yPosition = drawer.getLocalPoint(station.getPosition()).y-height+station.getTextRelativePosition().y;
			
			rect = new Rectangle(xPosition, yPosition, width, height);			
		}
	}
	
	public StandardDrawer() {
		
		//coordinateTransform = 15*scale;
		radius = (int)(scale);
	}
	@Override
	public void draw(List<EdiStation> stationSelection, List<EdiTunnel> tunnelSelection, Graphics g, boolean showNames) {
		polygons = new ArrayList<PolyStruct>();
		names = new ArrayList<NameStruct>();
		super.draw(stationSelection, tunnelSelection, g,showNames);		
		
		  
	}
	
	@Override
	public void draw(EdiStation station, Graphics g) {
		Point center = getLocalPoint(station.getPosition());		
		g.setFont(namesFont);
		g.setColor(Color.black);
		g.fillOval(center.x-radius, center.y-radius, radius*2, radius*2);
		if(showNames && station.isLabelVisible()) {
			names.add(new NameStruct(station,g));
			float t = 0.9f;
			g.setColor(new Color((int)(255*t), (int)(255*t),(int)(255*t),180));
			NameStruct ns = new NameStruct(station,g);
			((Graphics2D)g).fill(ns.rect);
			g.setColor(Color.black);
			((Graphics2D)g).draw(ns.rect);
			g.drawString(station.getName(), center.x + station.getTextRelativePosition().x, center.y+station.getTextRelativePosition().y);
			Point upperRight = new Point(ns.rect.x+ns.rect.width,ns.rect.y);
			Point upperLeft = ns.rect.getLocation();
			Point downRight = new Point(ns.rect.x+ns.rect.width,ns.rect.y+ns.rect.height);
			Point downLeft = new Point(ns.rect.x,ns.rect.y+ns.rect.height);
			Point rectCenter = new Point(ns.rect.x+ns.rect.width/2,ns.rect.y+ns.rect.height/2);

			Point lineTo = pointWithMinDistanse(rectCenter,
					pointWithMinDistanse(
							pointWithMinDistanse(upperRight,downRight,center), 
							pointWithMinDistanse(downLeft,upperLeft,center),
							center),center);

			((Graphics2D)g).setStroke(new BasicStroke(2));
			g.drawLine(center.x, center.y, lineTo.x, lineTo.y);
			((Graphics2D)g).setStroke(new BasicStroke());

		}
	}
	
	private Point pointWithMinDistanse(Point from1,Point from2, Point to) {
		if(Point.distance(from1.x, from1.y, to.x, to.y)<Point.distance(from2.x, from2.y, to.x, to.y)) {
			return from1;
		} else {
			return from2;
		}
			
	}

	
	public void draw(Transfer transfer, Graphics g) {		
		Point from = getLocalPoint(transfer.getFromStation().getPosition());
		Point to = getLocalPoint(transfer.getToStation().getPosition());
        int radius=(int)(this.radius*1.3f);       
        
        saveGrpahicsParam(g);
        g.setColor(Color.BLACK);
        
		((Graphics2D)g).setStroke(transferStroke1);
        g.drawLine(from.x, from.y, to.x, to.y);
        
        ((Graphics2D)g).setStroke(transferStroke2);
        g.drawOval(from.x-radius, from.y-radius, radius*2, radius*2);
        g.drawOval(to.x-radius-1, to.y-radius-1, radius*2, radius*2);        
       
        ((Graphics2D)g).setStroke(transferStroke3);
        
        g.setColor(Color.white);
        g.fillOval(from.x-radius, from.y-radius, radius*2, radius*2);
        g.fillOval(to.x-radius, to.y-radius, radius*2, radius*2); 
        
        ((Graphics2D)g).setStroke(transferStroke4);        
        g.drawLine(from.x, from.y, to.x, to.y);        
        
        g.setColor(Color.WHITE);
        restoreGraphicsParam(g);

		
	}

	
	public void draw(EdiTunnel tunnel, Graphics g) {
		Point from = getLocalPoint(tunnel.getFromStation().getPosition());
		Point to = getLocalPoint(tunnel.getToStation().getPosition());		
		polygons.add(new PolyStruct(tunnel,getPolygon(from,to,tunnelThikness+1)));
		saveGrpahicsParam(g);		
		
		((Graphics2D)g).setStroke(tunnelStroke);
		g.setColor(new Color(tunnel.getLine().getColor()));
		g.drawLine(from.x, from.y, to.x, to.y);		
		restoreGraphicsParam(g);
		
	}
	
	private Polygon getPolygon(Point start, Point end, float lineHeight) {
		Polygon pl= new Polygon();
		int dx=Math.abs(start.x-end.x);
		if(dx==0) {
			pl.addPoint(start.x-(int)lineHeight/2,start.y);
			pl.addPoint(start.x+(int)lineHeight/2,start.y);
			pl.addPoint(start.x+(int)lineHeight/2,end.y);
			pl.addPoint(start.x-(int)lineHeight/2,end.y);
			return pl;
		}
		int dy=Math.abs(start.y-end.y);
		double cos;
		double pdy, pdx;
		double tg = dy/(double)dx;		
		
		cos = dx/Math.sqrt(dx*dx+dy*dy);
		
		if((start.x-end.x)*(start.y-end.y)>0) {
			pdy = (cos*(lineHeight/2d)); 
			pdx = (tg*pdy);
		} else {
			pdy = -(cos*(lineHeight/2d)); 
			pdx = -(tg*pdy);
		}
		
		
		//now is not full tunnel(length is shorted by height/2 in both sides)
		//to solve it uncomment next block
		
		/*if((s.y-e.y)<0) {
			s = new Point(s.x-(int)pdy,(int)(s.y-pdx));
			e = new Point(e.x+(int)(pdy),(int)(e.y+pdx));
		} else {
			s = new Point(s.x+(int)pdy,(int)(s.y+pdx));
			e = new Point(e.x-(int)pdy,(int)(e.y-pdx));
		}*/
		
		pl.addPoint(start.x-(int)pdx, start.y+(int)pdy);
		pl.addPoint(start.x+(int)pdx, start.y-(int)pdy);
		pl.addPoint(end.x+(int)pdx, end.y-(int)pdy);
		pl.addPoint(end.x-(int)pdx, end.y+(int)pdy);
		return pl;
	}

	
	public Point getLocalPoint(Coordinates p) {
		return new Point((int)(p.x*scale+this.drawerCenter.x+xTranslate*scale),(int)(p.y*scale+this.drawerCenter.y+yTranslate*scale)); 
	}
	public Coordinates getAbsolutePoint(Point local) {
		return new Coordinates((int)((local.x-this.drawerCenter.x)/scale-xTranslate),(int)((local.y-this.drawerCenter.y)/scale-yTranslate)); 
	}


	public void shift(int dx, int dy) {
		xTranslate+=dx/scale;
		yTranslate+=dy/scale;
		//drawerCenter = new Point(drawerCenter.x+dx,drawerCenter.y+dy);
	}


	@Override
	public void drawSelectedStation(Station station, Graphics g) {
		Point center = getLocalPoint(station.getPosition());		
		saveGrpahicsParam(g);
		g.setColor(new Color(255,0,0,128));
		
		g.fillOval((int)(center.x-radius*SelectionRadiusTransform), (int)(center.y-radius*SelectionRadiusTransform), (int)(radius*SelectionRadiusTransform*2), (int)(radius*SelectionRadiusTransform*2));
		restoreGraphicsParam(g);
	}
	
	@Override
	public void drawSelectedTunnel(Tunnel tunnel, Graphics g) {
		Point from = getLocalPoint(tunnel.getFromStation().getPosition());
		Point to = getLocalPoint(tunnel.getToStation().getPosition());
		
		saveGrpahicsParam(g);		
		((Graphics2D)g).setStroke(selectedTunnelStroke);
		g.setColor(new Color(255,0,0,64));
		
		g.drawLine(from.x, from.y, to.x, to.y);		
		restoreGraphicsParam(g);
		
	}
	
	@Override
	public List<EdiStation> getStation(Point point) {
		List<EdiStation> stations = new LinkedList<EdiStation>(); 
		for(EdiStation station: MetroModel.getInstance()) {
			if(distance(point, this.getLocalPoint(station.getPosition()))<this.radius) {
				stations.add(station);
			}
		}
		return stations;
	}
	
	private double distance(Point a, Point b) {
		return java.awt.Point.distance(a.x, a.y, b.x, b.y);
	}


	@Override
	public Rectangle getRedrawRect(EdiStation station) {
		Point center = getLocalPoint(station.getPosition());		
		//TODO now station name label is not counting. need to be fixed
		Rectangle rect = new Rectangle((int)(center.x-radius*SelectionRadiusTransform), (int)(center.y-radius*SelectionRadiusTransform), (int)(radius*SelectionRadiusTransform*2), (int)(radius*SelectionRadiusTransform*2));
		return rect;
	}


	@Override
	public Rectangle getRedrawRect(Transition trans) {
		Point center1 = getLocalPoint(trans.getFromStation().getPosition());
		Point center2 = getLocalPoint(trans.getToStation().getPosition());
		int width = center1.x-center2.x;
		int height = center1.y-center2.y;
		
		Rectangle rect = new Rectangle(center2.x, center2.y, width, height);
		return rect;
	}


	@Override
	public float getScale() {
		return scale;		
	}


	
	@Override
	
	public void setScale(float scale) {
		
		this.scale = scale;
		
		
		
		
	}

	@Override
	public List<EdiTunnel> getTunnel(Point point) {
		List<EdiTunnel> list = new LinkedList<EdiTunnel>();
		for(PolyStruct ps: polygons) {
			if(ps.polygon.contains(point)) list.add(ps.tunnel);
		}
		return list;
	}	
	
	
	
	
	@Override
	public EdiStation getLabelsStation(Point point)  {
		for(NameStruct ns:names) {
			if(ns.rect.contains(point)) return ns.station;
		}
		return null;
	}
	@Override
	public void setDrawerCenter(int x, int y) {
		drawerCenter = new Point(x,y);
		
	}
	
	
	
	
	
	
	
}
